#ifndef TIME_INTERVALL_H
#define TIME_INTERVALL_H
//#include"stdafx.h"
using namespace std;
// diese Klasse beschreibt ein Intervall, dass durch "start" und "end" definiert wird; wobei end nicht mehr zum intervall gehört
// Bedingungen: start sollte demzufolge kleiner als "end" sein
template <typename T, typename U > class intervall
{
protected:
	T start;
	T end;
public:
	intervall()
	{
		
	}
	intervall(T istart,T iend)
	{
		
		start=istart;
		end=iend;
		
	}
	intervall& operator=(const intervall &inva)
		  {
				this->start=inva.start;
				this->end=inva.end;
				return *this;
		  }
	~intervall(){;}
	//operator
	int operator==(const intervall &inva)
    {
            
            int dec=0;
			//1. //xxxxx|-------|xxxx
			//2. //xxx|----|xxxx
			//3. //xxx|---------|xxxxx
			//4. //xxx|-------------|xxx
			if((this->start>=inva.start)&&(this->start<inva.end)) dec=1;
			if((inva.start>=this->start)&&(inva.start<this->end)) dec=1;
            return dec;
	}  
	int operator==(const intervall &time) const
    {
            
            int dec=0;
			//1. //xxxxx|-------|xxxx
			//2. //xxx|----|xxxx
			//3. //xxx|---------|xxxxx
			//4. //xxx|-------------|xxx
			if((this->start>=time.start)&&(this->start<time.end)) dec=1;
			if((time.start>=this->start)&&(time.start<this->end)) dec=1;
            return dec;
	}  
    int operator<(const intervall &inva)
    {
        int dec=0;
        if(this->end<=inva.start)dec=1;
        return dec;
    }
	int operator<(const intervall &inva) const
    {
        int dec=0;
        if(this->end<=inva.start)dec=1;
        return dec;
    }	
    int operator>(const intervall &inva)
    {
        if(this->start>=inva.end) return 1;
        return 0;
    }
	int operator>(const intervall &inva) const
    {
        if(this->start>=inva.end) return 1;
        return 0;
    }
	int operator>=(const intervall &inva)
    {
       if ((*this>inva)||(*this==inva)) return 1;
        return 0;
    }
	int operator>=(const intervall &inva) const
    {
        if ((*this>inva)||(*this==inva)) return 1;
        return 0;
    }
	int operator<=(const intervall &inva)
    {
		if ((*this<inva)||(*this==inva)) return 1;
        return 0;
    }
	int operator<=(const intervall &inva) const
    {
       if ((*this<inva)||(*this==inva)) return 1;
        return 0;
    }
    int operator!=(const intervall &inva)
    {
        return !(this->operator ==(inva));
    }   
	int contains(const T &ti)
	{
		return ((this->start<=ti)&&(this->end>ti));
	}
	int contains_plus_end(const T &ti)
	{
		return ((this->start<=ti)&&(this->end>ti));
	}
	int contain(const intervall &time)
	{
		if((this->contains(time.get_start()))&&
			((this->contains(time.get_end()))||(this->get_end()==time.get_end()))) {
			return 1;
		}
		return 0;
	}
	int overlap(const intervall &ti)
	{
            // returns one if intervalls overlap 
            // otherwise 0 (also when intervalls match at one border)
		if (this->get_end()<ti.get_start()) return 0;
                if (this->get_start()>ti.get_end()) return 0;
		
                return 1;
		
	}
	//set
	int set(T istart,T iend){
		start=istart;
		end=iend;
		return this->check_myself();
	}
	int set_start(T startdate)
	{
		start=startdate;
		return check_myself();
	}
	int set_end(T prec)
	{
		end=prec;
		
		return check_myself();
	}
	
	//get 
	T get_start() const
	{
		return start;
	}
	T get_end() const
	{
		return end;
	}
       
	int get_identity(intervall source)
	{
		if ((this->get_start()==source.get_start())&&(this->get_end()==source.get_end())) return 1;
		return 0;
	}
	U get_intersect( U &source)
	{
		// returns the intersection intervall
		T start,end;
		if (this->overlap(source)){
			 start=Basics_math::max_(this->get_start(),source.get_start());
			 end=Basics_math::min_(this->get_end(),source.get_end());
			 U i(start,end);
			 return i;
		}
		//err::err2("intervall::get_intersect: intervalls do not overlap.");
		U i(timestamp (0),timestamp(0));
		return i;
	}
        
        int check_myself(){
		int dec=0;
		
		if(start>end) {
			dec++;
			err::err3("time_intervall::check_myself: start>=end.");}
		return dec;
	}
}  ;
class time_intervall: public intervall <timestamp,time_intervall>
{
	vector<string> intervall_mode_list;
public:
	time_intervall()
	{
		read_intervall_mode_list();
		
		
	}
	time_intervall(timestamp istart,timestamp iend):intervall(istart,iend)
	{
		check_myself();
	}
	~time_intervall(){;}

	//logic
	
	//set
	int set(timestamp istart,timestamp iend){
		start=istart;
		end=iend;
		return this->check_myself();
	}
	int set_start_zus(timestamp prec,string intervallmode)
	{
		// sets start;in dependence on intervallmode it sets the "floor" of prec
		if(intervall_mode_list[0]==intervallmode){	//constant
			start=prec;}
		else if(intervall_mode_list[1]==intervallmode){ //day
			this->start.set_floor_day(prec);}
		else if(intervall_mode_list[2]==intervallmode){ //mon
			this->start.set_floor_mon(prec);
			this->start.set(prec.get_year(),prec.get_mon(),1.0);}
		else if(intervall_mode_list[3]==intervallmode){ //year
			this->start.set_floor_year(prec);
			this->start.set(prec.get_year(),1,1.0);
		}
		else err::err3("time_intervall:set_start::intervallmode ill defined."+ intervallmode);
		return 0;
	}
	int set_end_zus(string intervallmode,int multipli,double diff)
	{
		if(intervall_mode_list[0]==intervallmode){	//constant
			this->end=this->start;
			this->end.set_add(diff);}
		else if(intervall_mode_list[1]==intervallmode){ //day
			this->end=this->start;
			this->end.set_add_days(multipli);}
		else if(intervall_mode_list[2]==intervallmode){ //mon
			this->end=this->start;
			this->end.set_add_months(multipli);}
		else if(intervall_mode_list[3]==intervallmode){ //year
			this->end=this->start;
			this->end.set_add_years(multipli);
		}
		return 0;
	}
	int set_start(timestamp startdate)
	{
		start=startdate;
		return check_myself();
	}
	int set_end(timestamp prec)
	{
		end=prec;
		
		return check_myself();
	}
	int set_subsequent(time_intervall previous,string intervallmode,int multipli)
	{
		
		double diff;
		int dec=0;
		if(intervall_mode_list[0]==intervallmode){	//constant
			diff=previous.start.get_dist(previous.get_end());
			this->start=previous.end;
			this->end=this->start.set_add(diff);
		}
		else if(intervall_mode_list[1]==intervallmode){ //day
			this->start.set_floor_day(previous.get_end());
			this->end=this->start;
			this->end.set_add_days(multipli);
		}
		else if(intervall_mode_list[2]==intervallmode){ //mon
			this->start.set_floor_mon(previous.get_end());
			this->end=this->start;
			this->end.set_add_months(multipli);
		}
		else if(intervall_mode_list[3]==intervallmode){ //year
			this->start.set_floor_year(previous.get_end());
			this->end=this->start;
			this->end.set_add_years(multipli);
		}
		check_myself();
		if (previous==*this) dec=1;
		return dec;
	}
	int set_move(double diff)
	{
		start.set_add(diff);
		end.set_add(diff);
		return check_myself();
	}	
	int set_move_year(int diff)
	{
		start.set_add_years(diff);
		end.set_add_years(diff);
		return check_myself();
	}
	//get 
	
	double get_dur() const
	{
		return start.get_dist(end);
	}
	
	timestamp get_mid() const
	{
		timestamp ts;
		ts=start;
		ts.set_add(ts.get_dist(end)/2);
		return ts;
	}
	int get_identity(time_intervall source)
	{
            //returns 1 if time_intervalls are equal
		if ((this->get_start()==source.get_start())&&(this->get_end()==source.get_end())) return 1;
		return 0;
	}
	double get_dur_by_intersect(time_intervall ti)
	{
		//calculates the duration of overlapping time_intervall
		time_intervall d;
		d=this->get_intersect(ti);
		return d.get_dur();
	}
	int check_myself(){
		int dec=0;
		dec=dec+start.check_myself();
		dec=dec+end.check_myself();
		if(start>end) {
			dec++;
			err::err3("time_intervall::check_myself: start>=end.");}
		return dec;
	}

	int read_intervall_mode_list()
	{
		intervall_mode_list.push_back("constant");
		intervall_mode_list.push_back("day");
		intervall_mode_list.push_back("mon");
		intervall_mode_list.push_back("year");
		return 0;
	}
};
class double_intervall: public intervall <double,double>
{
	double get_dur()
	{
		return end-start;
	}
};
#endif